Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 688885190c95bf53006a2d5ac5500b6644a260a6


Parents : db4123a
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-04T17:13:07-05:00

refactor(backend): improve error handling for identity management and improve type safety across various modules

Changes
Diff

diff --git a/meshchatx/src/backend/bot_handler.py b/meshchatx/src/backend/bot_handler.py
index 87ae310e..43b1a715 100644
--- a/meshchatx/src/backend/bot_handler.py
+++ b/meshchatx/src/backend/bot_handler.py
@@ -223,7 +223,13 @@ class BotHandler:
identity = self._load_identity_for_bot(bot_id)
if identity:
with contextlib.suppress(Exception):
- destination = RNS.Destination(identity, "lxmf", "delivery")
+ destination = RNS.Destination(
+ identity,
+ RNS.Destination.OUT,
+ RNS.Destination.SINGLE,
+ "lxmf",
+ "delivery",
+ )
address_full = self._normalize_lxmf_hash_hex(destination.hash)
if address_full:
address_pretty = RNS.prettyhexrep(

diff --git a/meshchatx/src/backend/database/__init__.py b/meshchatx/src/backend/database/__init__.py
index 501e14f0..9e28d1a6 100644
--- a/meshchatx/src/backend/database/__init__.py
+++ b/meshchatx/src/backend/database/__init__.py
@@ -301,8 +301,12 @@ class Database:
free_bytes = freelist_bytes
else:
try:
- db_dir = os.path.dirname(os.path.abspath(self.provider.db_path))
- free_bytes = shutil.disk_usage(db_dir).free if db_dir else 0
+ db_path = self.provider.db_path
+ if not db_path:
+ free_bytes = 0
+ else:
+ db_dir = os.path.dirname(os.path.abspath(db_path))
+ free_bytes = shutil.disk_usage(db_dir).free if db_dir else 0
except OSError:
free_bytes = 0
@@ -392,7 +396,11 @@ class Database:
self.provider.close_all()
def _identity_storage_dir(self) -> str:
- return os.path.dirname(os.path.abspath(self.provider.db_path))
+ db_path = self.provider.db_path
+ if not db_path:
+ msg = "database path is not configured"
+ raise ValueError(msg)
+ return os.path.dirname(os.path.abspath(db_path))
def _add_identity_storage_to_zip(
self,

diff --git a/meshchatx/src/backend/database/gifs.py b/meshchatx/src/backend/database/gifs.py
index 4c91ae56..150bb2a0 100644
--- a/meshchatx/src/backend/database/gifs.py
+++ b/meshchatx/src/backend/database/gifs.py
@@ -194,6 +194,10 @@ class UserGifsDAO:
usage = int(item.get("usage_count") or 0)
usage = max(usage, 0)
try:
+ if not isinstance(b64, (str, bytes)) or not b64:
+ skipped_invalid += 1
+ errors.append(f"decode_failed_at_{i}")
+ continue
raw = base64.b64decode(b64, validate=False)
except (ValueError, TypeError):
skipped_invalid += 1

diff --git a/meshchatx/src/backend/database/provider.py b/meshchatx/src/backend/database/provider.py
index 3f686e40..857bb406 100644
--- a/meshchatx/src/backend/database/provider.py
+++ b/meshchatx/src/backend/database/provider.py
@@ -69,6 +69,9 @@ class DatabaseProvider:
return self._memory_connection
if not hasattr(self._local, "connection"):
+ if self.db_path is None:
+ msg = "db_path is required for database connections"
+ raise ValueError(msg)
# isolation_level=None enables autocommit mode, letting us manage transactions manually
self._local.connection = sqlite3.connect(
self.db_path,

diff --git a/meshchatx/src/backend/database/stickers.py b/meshchatx/src/backend/database/stickers.py
index 15c641ab..4bc65db0 100644
--- a/meshchatx/src/backend/database/stickers.py
+++ b/meshchatx/src/backend/database/stickers.py
@@ -323,6 +323,10 @@ class UserStickersDAO:
src = item.get("source_message_hash")
emoji = sticker_utils.sanitize_sticker_emoji(item.get("emoji"))
try:
+ if not isinstance(b64, (str, bytes)) or not b64:
+ skipped_invalid += 1
+ errors.append(f"decode_failed_at_{i}")
+ continue
raw = base64.b64decode(b64, validate=False)
except (ValueError, TypeError):
skipped_invalid += 1

diff --git a/meshchatx/src/backend/diagnostics/memory_diagnostics.py b/meshchatx/src/backend/diagnostics/memory_diagnostics.py
index bcba72e5..e38a9cf5 100644
--- a/meshchatx/src/backend/diagnostics/memory_diagnostics.py
+++ b/meshchatx/src/backend/diagnostics/memory_diagnostics.py
@@ -454,7 +454,7 @@ class MemoryDiagnostics:
},
]
- def gc_garbage_types(self) -> list[dict[str, int]]:
+ def gc_garbage_types(self) -> list[dict[str, str | int]]:
"""Show types of objects in gc.garbage (uncollectable objects)."""
counts: dict[str, int] = {}
try:

diff --git a/meshchatx/src/backend/forwarding_manager.py b/meshchatx/src/backend/forwarding_manager.py
index 0577363c..e86ceef2 100644
--- a/meshchatx/src/backend/forwarding_manager.py
+++ b/meshchatx/src/backend/forwarding_manager.py
@@ -101,9 +101,14 @@ class ForwardingManager:
self.forwarding_destinations[alias_hash] = alias_destination
self.forwarding_routers[alias_hash] = router
+ private_key = alias_identity.get_private_key()
+ if not private_key:
+ msg = "alias identity has no private key"
+ raise ValueError(msg)
+
data = {
"alias_identity_private_key": base64.b64encode(
- alias_identity.get_private_key(),
+ private_key,
).decode(),
"alias_hash": alias_hash,
"original_sender_hash": source_hash,

diff --git a/meshchatx/src/backend/identity_context.py b/meshchatx/src/backend/identity_context.py
index 94207623..36dd5234 100644
--- a/meshchatx/src/backend/identity_context.py
+++ b/meshchatx/src/backend/identity_context.py
@@ -56,8 +56,12 @@ class IdentityContext:
# Identity backup
identity_backup_file = os.path.join(self.storage_path, "identity")
if not os.path.exists(identity_backup_file):
+ private_key = identity.get_private_key()
+ if not private_key:
+ msg = "identity has no private key"
+ raise ValueError(msg)
with open(identity_backup_file, "wb") as f:
- f.write(identity.get_private_key())
+ f.write(private_key)
# Session ID for this specific context instance
if not hasattr(app, "_identity_session_id_counter"):
@@ -326,8 +330,12 @@ class IdentityContext:
print(f"Failed to restore bots: {exc}")
# Initialize managers
+ identity = self.identity
+ if identity is None:
+ msg = "identity is required for manager setup"
+ raise RuntimeError(msg)
self.telephone_manager = TelephoneManager(
- self.identity,
+ identity,
config_manager=self.config,
storage_dir=self.storage_path,
db=self.database,

diff --git a/meshchatx/src/backend/identity_manager.py b/meshchatx/src/backend/identity_manager.py
index 78a736e5..4d406fdf 100644
--- a/meshchatx/src/backend/identity_manager.py
+++ b/meshchatx/src/backend/identity_manager.py
@@ -19,7 +19,11 @@ class IdentityManager:
self.identity_file_path = identity_file_path
def get_identity_bytes(self, identity: RNS.Identity) -> bytes:
- return identity.get_private_key()
+ private_key = identity.get_private_key()
+ if not private_key:
+ msg = "identity has no private key"
+ raise ValueError(msg)
+ return private_key
def backup_identity(self, identity: RNS.Identity) -> dict:
identity_bytes = self.get_identity_bytes(identity)

diff --git a/meshchatx/src/backend/interface_config_parser.py b/meshchatx/src/backend/interface_config_parser.py
index e66cb597..4e1fb104 100644
--- a/meshchatx/src/backend/interface_config_parser.py
+++ b/meshchatx/src/backend/interface_config_parser.py
@@ -29,9 +29,8 @@ class InterfaceConfigParser:
# process interfaces
interfaces = []
- for interface_name in config_interfaces:
+ for interface_name, interface_config in config_interfaces.items():
# ensure interface has a name
- interface_config = config_interfaces[interface_name]
if not isinstance(interface_config, dict):
print(
f"Skipping invalid interface configuration for {interface_name}: expected dict, got {type(interface_config)}",

diff --git a/meshchatx/src/backend/interface_editor.py b/meshchatx/src/backend/interface_editor.py
index 814beff5..c89b39e2 100644
--- a/meshchatx/src/backend/interface_editor.py
+++ b/meshchatx/src/backend/interface_editor.py
@@ -90,6 +90,8 @@ def validate_rnode_txpower(value) -> str | None:
power = normalize_rnode_txpower(value)
except (TypeError, ValueError):
return "TX power must be an integer dBm value"
+ if not isinstance(power, int):
+ return "TX power must be an integer dBm value"
if power < RNODE_TXPOWER_MIN or power > RNODE_TXPOWER_MAX:
return (
f"TX power must be between {RNODE_TXPOWER_MIN} and {RNODE_TXPOWER_MAX} dBm "

diff --git a/meshchatx/src/backend/interface_port_check.py b/meshchatx/src/backend/interface_port_check.py
index 6afb4e2c..3905b20e 100644
--- a/meshchatx/src/backend/interface_port_check.py
+++ b/meshchatx/src/backend/interface_port_check.py
@@ -61,7 +61,7 @@ def is_port_in_use(host: str | None, port, *, kind: str = "tcp") -> bool:
sock_kind = socket.SOCK_DGRAM if str(kind).lower() == "udp" else socket.SOCK_STREAM
normalized = _normalize_host(host)
- candidates: list[tuple[int, str]] = []
+ candidates: list[tuple[socket.AddressFamily, str]] = []
if normalized == "":
candidates.append((socket.AF_INET, "0.0.0.0")) # noqa: S104
candidates.append((socket.AF_INET6, "::"))
@@ -74,12 +74,12 @@ def is_port_in_use(host: str | None, port, *, kind: str = "tcp") -> bool:
)
except OSError:
return False
- seen: set[tuple[int, str]] = set()
+ seen: set[tuple[socket.AddressFamily, str]] = set()
for info in infos:
family = info[0]
if family not in (socket.AF_INET, socket.AF_INET6):
continue
- address = info[4][0]
+ address = str(info[4][0])
key = (family, address)
if key in seen:
continue

diff --git a/meshchatx/src/backend/interfaces/WebsocketClientInterface.py b/meshchatx/src/backend/interfaces/WebsocketClientInterface.py
index 14c36f1b..cb35af52 100644
--- a/meshchatx/src/backend/interfaces/WebsocketClientInterface.py
+++ b/meshchatx/src/backend/interfaces/WebsocketClientInterface.py
@@ -17,7 +17,7 @@ class WebsocketClientInterface(Interface):
def __str__(self):
return f"WebsocketClientInterface[{self.name}/{self.target_url}]"
- def __init__(self, owner, configuration, websocket: Connection = None):
+ def __init__(self, owner, configuration, websocket: Connection | None = None):
super().__init__()
self.owner = owner
@@ -113,8 +113,13 @@ class WebsocketClientInterface(Interface):
def read_loop(self):
self.online = True
+ websocket = self.websocket
+ if websocket is None:
+ self.online = False
+ return
+
try:
- for message in self.websocket:
+ for message in websocket:
self.process_incoming(message)
except Exception as e:
RNS.log(f"{self} read loop error: {e}", RNS.LOG_ERROR)

diff --git a/meshchatx/src/backend/interfaces/WebsocketServerInterface.py b/meshchatx/src/backend/interfaces/WebsocketServerInterface.py
index 2b7a548e..e3acb006 100644
--- a/meshchatx/src/backend/interfaces/WebsocketServerInterface.py
+++ b/meshchatx/src/backend/interfaces/WebsocketServerInterface.py
@@ -34,26 +34,26 @@ class WebsocketServerInterface(Interface):
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
self.server: Server | None = None
- self.spawned_interfaces: [WebsocketClientInterface] = []
+ self.spawned_interfaces: list[WebsocketClientInterface] = []
# parse config
ifconf = Interface.get_config_obj(configuration)
self.name = ifconf.get("name")
- self.listen_ip = ifconf.get("listen_ip", None)
- self.listen_port = ifconf.get("listen_port", None)
+ listen_ip = ifconf.get("listen_ip", None)
+ listen_port = ifconf.get("listen_port", None)
# ensure listen ip is provided
- if self.listen_ip is None:
+ if listen_ip is None:
msg = f"listen_ip is required for interface '{self.name}'"
raise SystemError(msg)
# ensure listen port is provided
- if self.listen_port is None:
+ if listen_port is None:
msg = f"listen_port is required for interface '{self.name}'"
raise SystemError(msg)
- # convert listen port to int
- self.listen_port = int(self.listen_port)
+ self.listen_ip = str(listen_ip)
+ self.listen_port = int(str(listen_port).strip())
# run websocket server
thread = threading.Thread(target=self.serve)

diff --git a/meshchatx/src/backend/legacy_migrator.py b/meshchatx/src/backend/legacy_migrator.py
index 6247b5ec..e2f98808 100644
--- a/meshchatx/src/backend/legacy_migrator.py
+++ b/meshchatx/src/backend/legacy_migrator.py
@@ -169,8 +169,12 @@ def fresh_storage_at_target(target_path: str) -> None:
raise ValueError("target not empty")
os.makedirs(target_path, exist_ok=True)
ident = RNS.Identity(create_keys=True)
+ private_key = ident.get_private_key()
+ if not private_key:
+ msg = "failed to create identity private key"
+ raise ValueError(msg)
with open(os.path.join(target_path, "identity"), "wb") as f:
- f.write(ident.get_private_key())
+ f.write(private_key)
def assert_migration_context_paths(ctx: dict, legacy: str, target: str) -> None:

diff --git a/meshchatx/src/backend/lxmf_sieve.py b/meshchatx/src/backend/lxmf_sieve.py
index 20fc8229..312cef39 100644
--- a/meshchatx/src/backend/lxmf_sieve.py
+++ b/meshchatx/src/backend/lxmf_sieve.py
@@ -91,8 +91,11 @@ def normalize_lxmf_sieve_filters(filters: list) -> list[dict[str, Any]]:
folder_id: int | None = None
if action == "folder":
+ raw_folder_id = item.get("folder_id")
+ if raw_folder_id is None:
+ continue
try:
- folder_id = int(item.get("folder_id"))
+ folder_id = int(raw_folder_id)
except (TypeError, ValueError):
continue

diff --git a/meshchatx/src/backend/nomadnet_downloader.py b/meshchatx/src/backend/nomadnet_downloader.py
index 8b9ff6d1..40af285a 100644
--- a/meshchatx/src/backend/nomadnet_downloader.py
+++ b/meshchatx/src/backend/nomadnet_downloader.py
@@ -10,6 +10,7 @@ from collections.abc import Callable
import RNS
from meshchatx.src.backend import reticulum_pathfinding
+from meshchatx.src.backend.reticulum_pathfinding import ReticulumLike
# Global cache for Nomad Network links (reuse instead of reconnecting per request).
# Protected by _nomadnet_links_lock for callers that may touch Reticulum from multiple threads.
@@ -77,7 +78,7 @@ class NomadnetDownloader:
timeout: int | None = None,
*,
on_phase: Callable[[str], None] | None = None,
- reticulum: object | None = None,
+ reticulum: ReticulumLike | None = None,
):
self.app_name = "nomadnetwork"
self.aspects = "node"
@@ -257,7 +258,7 @@ class NomadnetPageDownloader(NomadnetDownloader):
timeout: int | None = None,
*,
on_phase: Callable[[str], None] | None = None,
- reticulum: object | None = None,
+ reticulum: ReticulumLike | None = None,
):
self.on_page_download_success = on_page_download_success
self.on_page_download_failure = on_page_download_failure
@@ -301,7 +302,7 @@ class NomadnetFileDownloader(NomadnetDownloader):
timeout: int | None = None,
*,
on_phase: Callable[[str], None] | None = None,
- reticulum: object | None = None,
+ reticulum: ReticulumLike | None = None,
):
self.on_file_download_success = on_file_download_success
self.on_file_download_failure = on_file_download_failure
@@ -323,16 +324,16 @@ class NomadnetFileDownloader(NomadnetDownloader):
if isinstance(response, io.BufferedReader):
file_name = "downloaded_file"
metadata = request_receipt.metadata
- if metadata is not None and "name" in metadata:
+ if isinstance(metadata, dict) and "name" in metadata:
try:
file_path = metadata["name"].decode("utf-8", errors="replace")
file_name = os.path.basename(file_path)
except (AttributeError, TypeError):
pass
- file_data: bytes = response.read()
+ payload = response.read()
- self.on_file_download_success(file_name, file_data)
+ self.on_file_download_success(file_name, payload)
return
if (
@@ -340,24 +341,24 @@ class NomadnetFileDownloader(NomadnetDownloader):
and len(response) > 1
and isinstance(response[1], dict)
):
- file_data: bytes = response[0]
- metadata: dict = response[1]
+ payload = response[0]
+ metadata = response[1]
file_name = "downloaded_file"
- if metadata is not None and "name" in metadata:
+ if "name" in metadata:
try:
file_path = metadata["name"].decode("utf-8", errors="replace")
file_name = os.path.basename(file_path)
except (AttributeError, TypeError):
pass
- self.on_file_download_success(file_name, file_data)
+ self.on_file_download_success(file_name, payload)
return
try:
- file_name: str = response[0]
- file_data: bytes = response[1]
- self.on_file_download_success(file_name, file_data)
+ file_name = str(response[0])
+ payload = response[1]
+ self.on_file_download_success(file_name, payload)
except Exception:
self.on_download_failure("unsupported_response")

diff --git a/meshchatx/src/backend/rnpath_handler.py b/meshchatx/src/backend/rnpath_handler.py
index 6caa5bf0..42c259f3 100644
--- a/meshchatx/src/backend/rnpath_handler.py
+++ b/meshchatx/src/backend/rnpath_handler.py
@@ -4,7 +4,7 @@ import RNS
class RNPathHandler:
- def __init__(self, reticulum_instance: RNS.Reticulum):
+ def __init__(self, reticulum_instance: RNS.Reticulum | None):
self.reticulum = reticulum_instance
def get_path_table(
@@ -136,9 +136,11 @@ class RNPathHandler:
return dropped
def drop_all_via(self, transport_instance_hash: str) -> bool:
+ if self.reticulum is None:
+ return False
try:
ti_bytes = bytes.fromhex(transport_instance_hash)
- return self.reticulum.drop_all_via(ti_bytes)
+ return bool(self.reticulum.drop_all_via(ti_bytes))
except Exception:
return False

diff --git a/meshchatx/src/backend/rnprobe_handler.py b/meshchatx/src/backend/rnprobe_handler.py
index 53eeaacb..0df71887 100644
--- a/meshchatx/src/backend/rnprobe_handler.py
+++ b/meshchatx/src/backend/rnprobe_handler.py
@@ -107,7 +107,8 @@ class RNProbeHandler:
try:
probe.pack()
except OSError as e:
- msg = f"Probe packet size of {len(probe.raw)} bytes exceeds MTU of {RNS.Reticulum.MTU} bytes"
+ raw = probe.raw or b""
+ msg = f"Probe packet size of {len(raw)} bytes exceeds MTU of {RNS.Reticulum.MTU} bytes"
raise ValueError(msg) from e
receipt = probe.send()

diff --git a/meshchatx/src/backend/rrc/manager.py b/meshchatx/src/backend/rrc/manager.py
index 999ac978..26cee162 100644
--- a/meshchatx/src/backend/rrc/manager.py
+++ b/meshchatx/src/backend/rrc/manager.py
@@ -901,6 +901,8 @@ class RRCHub:
if not isinstance(env, dict):
return
t = env.get(proto.K_T)
+ if t is None:
+ return
handler = self._PACKET_HANDLERS.get(t)
if handler is not None:

diff --git a/meshchatx/src/backend/telephone_manager.py b/meshchatx/src/backend/telephone_manager.py
index dd884042..0b23c46b 100644
--- a/meshchatx/src/backend/telephone_manager.py
+++ b/meshchatx/src/backend/telephone_manager.py
@@ -356,15 +356,18 @@ class TelephoneManager:
# FIXME: Remove telephony-destination pre-path lookup once LXST aligns
# identity-hash and telephony-destination path handling.
- call_destination_hash = destination_hash
+ call_destination_hash: bytes = destination_hash
with contextlib.suppress(Exception):
- call_destination_hash = RNS.Destination(
+ dest = RNS.Destination(
destination_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
"lxst",
"telephony",
- ).hash
+ )
+ dest_hash = dest.hash
+ if isinstance(dest_hash, bytes):
+ call_destination_hash = dest_hash
if not RNS.Transport.has_path(call_destination_hash):
self._update_initiation_status("Requesting path...")

diff --git a/meshchatx/src/backend/voicemail_manager.py b/meshchatx/src/backend/voicemail_manager.py
index 21c8b9ae..5c85ce63 100644
--- a/meshchatx/src/backend/voicemail_manager.py
+++ b/meshchatx/src/backend/voicemail_manager.py
@@ -412,7 +412,10 @@ class VoicemailManager:
return
try:
- duration = int(time.time() - self.recording_start_time)
+ if self.recording_start_time is None:
+ duration = 0
+ else:
+ duration = int(time.time() - self.recording_start_time)
if self.recording_pipeline:
self.recording_pipeline.stop()
@@ -424,7 +427,7 @@ class VoicemailManager:
self.recording_pipeline = None
# Save to database if long enough
- if duration >= 1:
+ if duration >= 1 and self.recording_filename:
filepath = os.path.join(self.recordings_dir, self.recording_filename)
self._fix_recording(filepath)
@@ -461,9 +464,13 @@ class VoicemailManager:
)
else:
# Delete short/empty recording
- filepath = os.path.join(self.recordings_dir, self.recording_filename)
- if os.path.exists(filepath):
- os.remove(filepath)
+ if self.recording_filename:
+ filepath = os.path.join(
+ self.recordings_dir,
+ self.recording_filename,
+ )
+ if os.path.exists(filepath):
+ os.remove(filepath)
self.is_recording = False
self.is_greeting_recording = False

diff --git a/meshchatx/src/backend/web_audio_bridge.py b/meshchatx/src/backend/web_audio_bridge.py
index 2425c676..d213b00d 100644
--- a/meshchatx/src/backend/web_audio_bridge.py
+++ b/meshchatx/src/backend/web_audio_bridge.py
@@ -207,6 +207,8 @@ class WebAudioBridge:
if self.rx_sink:
return
try:
+ if not self.loop:
+ return
send_fn = lambda pcm: self._send_bytes_to_all(pcm) # noqa: E731
self.rx_sink = WebAudioSink(self.loop, send_fn)
# Build tee with existing audio_output as first sink to preserve speaker

diff --git a/typings/argostranslate/__init__.pyi b/typings/argostranslate/__init__.pyi
new file mode 100644
index 00000000..099e999b
--- /dev/null
+++ b/typings/argostranslate/__init__.pyi
@@ -0,0 +1 @@
+# Optional dependency (translator); stub for type checking only.

diff --git a/typings/usbserial4a/__init__.pyi b/typings/usbserial4a/__init__.pyi
new file mode 100644
index 00000000..f95df7b6
--- /dev/null
+++ b/typings/usbserial4a/__init__.pyi
@@ -0,0 +1 @@
+# Optional Android dependency; stub for type checking only.


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────